/*
 * Password management for the Admin server
 *
 * @author Jonathan Walsh
 * @author Jesse Brown
 */

package mitm;

import java.io.IOException;
import java.security.GeneralSecurityException;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.util.Random;
import java.util.HashMap;
import java.io.*;

public class MITMPassword {

    /*
     * This is the password for the db which is used to encrypt the passwords
     */
    private static final char[] PASSWORD = "iwioeurcrcwmoiefmsoijdfkj".toCharArray();

    /*
     * Usage is either <passwordFile> <usr> <password> or no arguments which 
     *    puts the program into interactive mode.
     */
    public static void main(String[] args) throws Exception {
	
	String passwdFile, usr, passwd;
	
	if ( args.length != 3 ) {
	    System.out.println( "Insuffient args found, going interactive!" );
	    
	    BufferedReader in = new BufferedReader( new InputStreamReader(System.in) );
	    System.out.println("Enter password file name:");
	    passwdFile = in.readLine();
	    System.out.println("Enter username to add:");
	    usr = in.readLine();
	    System.out.println("Enter password to add:");
	    passwd = in.readLine();

	} else {
	    passwdFile = args[0];
	    usr = args[1];
	    passwd = args[2];
	}

	if ( usr.indexOf(":") != -1 ) {
	    System.out.println("No use of \":\" in username!" );
	    System.exit(1);
	}

	/* Generate a random salt for this encryption */
	Random r = new Random();
	byte[] salt = new byte[8];
	r.nextBytes(salt);

	BufferedWriter out = new BufferedWriter( new FileWriter( passwdFile, true ) );
	out.write(usr+":"+encode(salt)+":"+encrypt(passwd,salt)+"\n");
	out.close();

    }
    
    /*
     * Create a datastructure to store the passwords from the given password db
     *
     * @param passwdFile - The password database to read in
     */
    public static HashMap<String,String> getPasswords( String passwdFile ) throws IOException,GeneralSecurityException {
	HashMap<String,String> ret = new HashMap<String,String>();

	BufferedReader in = new BufferedReader( new FileReader( passwdFile ) );
	
	String line = in.readLine();
	while ( line != null ) {
	    int fcol = line.indexOf(":");
	    
	    String usr = line.substring(0,fcol);
	    String salt = line.substring(fcol+1,fcol+1+12);
	    String passwd = line.substring(fcol+1+12+1);

	    ret.put( usr, decrypt(passwd,decode(salt)) );

	    line = in.readLine();
	}

	return ret;
    }
    
    /*
     * encrypt the password with the salt provided
     *
     * @param passwd - The password to encrypt
     * @param salt - The salt to use
     */
    private static String encrypt(String passwd, byte[] salt) throws GeneralSecurityException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
        pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(salt, 20));
        return encode(pbeCipher.doFinal(passwd.getBytes()));
    }
        
    /*
     * Decrypt the password with the salt provided
     *
     * @param passwd - The password to decrypt
     * @param salt - The salt to use
     */
    private static String decrypt(String passwd,byte[] salt) throws GeneralSecurityException, IOException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
        pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(salt, 20));
        return new String(pbeCipher.doFinal(decode(passwd)));
    }

    /* Encode the bytes to a string - this gives a warning, but it is easier 
     * than writing it myself!*/
    private static String encode(byte[] bytes) {
	return new BASE64Encoder().encode(bytes);
    }
    /* Decode the strings from a byte */
    private static byte[] decode(String passwd) throws IOException {
    	return new BASE64Decoder().decodeBuffer(passwd);
    }
}
